package com.spsoft.core.utils;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.security.MessageDigest;
import java.sql.Blob;
import java.sql.SQLException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class GgUtil {
    /***
     * 日志记录
     */
    private static Log log = LogFactory.getLog(GgUtil.class);

    /***
     * 字典数据
     */
    public static String[] chars = new String[]
            {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o",
                    "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1",
                    "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E",
                    "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R",
                    "S", "T", "U", "V", "W", "X", "Y", "Z"};

    /**
     * @param format 格式
     * @return 当前时间戳
     * @description 获取指定格式的当前日期
     * @access 按照指定的格式输出当前日期戳
     * @precondition 无
     * @postcondition 无
     */
    public static String getCurrentDate(String format) {
        // 转换为日期格式化类
        SimpleDateFormat sdf = new SimpleDateFormat(format);
        sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
        return sdf.format(new Date());
    }

    /**
     * @return 当前时间戳
     * @description 获取指定格式的当前日期
     * @access 指定格式, 输出当前日期戳
     * @precondition 无
     * @postcondition 无
     */
    public static String dqsj() {
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
        return sdf.format(date).toString();
    }

    /**
     * @return 当前时间戳
     * @description 获取指定格式的当前日期
     * @access 指定格式, 输出当前日期戳
     * @precondition 无
     * @postcondition 无
     */
    public static String getCurrentTime() {
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
        sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
        return sdf.format(date);
    }

    public static String getCurrentTimeNew() {
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
        return sdf.format(date);
    }


    public static String getCurrentTimeNewOneDayBefore() {
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
        return sdf.format(date);
    }


    /**
     * @param dstr yyyy-MM-dd格式的时间戳
     * @return yyyyMMdd格式的时间戳
     * @throws Exception 格式不一致异常
     * @description 将yyyy-MM-dd格式的时间戳去掉中间的横线
     * @access 定义中间转换格式，实现日期格式的转换
     * @precondition 无
     * @postcondition 无
     */
    public static String covdate(String dstr) throws Exception {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Date date = sdf.parse(dstr);
        SimpleDateFormat sdf1 = new SimpleDateFormat("yyyyMMdd");
        return sdf1.format(date).toString();
    }

    public static byte[] base64Decode(String pContent) throws IOException
    {
        BASE64Decoder base64 = new BASE64Decoder();
        return base64.decodeBuffer(pContent);
    }
    /**
     * @param pBytes 待编码的数据
     * @return Base64编码后的数据
     * @description Base64编码
     * @access 调用Java现成的函数实现Base64编码
     * @precondition 无
     * @postcondition 无
     */
    public static String base64Encode(byte[] pBytes) {
        BASE64Encoder base64 = new BASE64Encoder();
        return base64.encode(pBytes);
    }

    /**
     * @param str 待编码的数据
     * @return Base64编码后的数据
     * @description 对字符串数据进行Base64编码
     * @access 获取字符串字符数组，然后对字符数据调用Java现成的函数实现Base64编码
     * @precondition 无
     * @postcondition 无
     */
    public static String base64Encode(String str) {
        try {
            // 对UTF-8格式的字符串进行编码处理
            return base64Encode(str.getBytes("utf-8"));
        } catch (UnsupportedEncodingException e) {
            log.error(e);
        }
        return null;
    }

    /**
     * @description 得到金额的大写表示字符串
     * @access 依次获取每位上的数据，然后查表获取数值大写
     * @precondition 无
     * @postcondition 无
     * @param val
     *            待转换的数据
     * @return 大写数值
     */
    /**
     * 汉语中数字大写
     */
    private static final String[] CN_UPPER_NUMBER =
            {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};

    /**
     * 汉语中货币单位大写，这样的设计类似于占位符
     */
    private static final String[] CN_UPPER_MONETRAY_UNIT =
            {"分", "角", "圆", "拾", "佰", "仟", "万", "拾", "佰", "仟", "亿", "拾", "佰", "仟", "兆",
                    "拾", "佰", "仟"};

    /**
     * 特殊字符：整
     */
    private static final String CN_FULL = "整";

    /**
     * 特殊字符：负
     */
    private static final String CN_NEGATIVE = "负";

    /**
     * 金额的精度，默认值为2
     */
    private static final int MONEY_PRECISION = 2;

    /**
     * 特殊字符：零元整
     */
    private static final String CN_ZEOR_FULL = "零圆" + CN_FULL;

    /**
     * @param numberOfMoney 待转换的数据
     * @return 大写数值
     * @description 得到金额的大写表示字符串
     * @access 依次获取每位上的数据，然后查表获取数值大写
     * @precondition 无
     * @postcondition 无
     */
    public static String number2CNMontrayUnit(BigDecimal numberOfMoney) {
        StringBuffer sb = new StringBuffer();
        // -1, 0, or 1 as the value of this BigDecimal is negative, zero, or
        // positive.
        int signum = numberOfMoney.signum();
        // 零元整的情况
        if (signum == 0) {
            return CN_ZEOR_FULL;
        }
        // 这里会进行金额的四舍五入
        long number = numberOfMoney.movePointRight(MONEY_PRECISION)
                .setScale(0, 4).abs().longValue();
        // 得到小数点后两位值
        long scale = number % 100;
        int numUnit = 0;
        int numIndex = 0;
        boolean getZero = false;
        // 判断最后两位数，一共有四中情况：00 = 0, 01 = 1, 10, 11
        if (!(scale > 0)) {
            numIndex = 2;
            number = number / 100;
            getZero = true;
        }
        if ((scale > 0) && (!(scale % 10 > 0))) {
            numIndex = 1;
            number = number / 10;
            getZero = true;
        }
        int zeroSize = 0;
        while (true) {
            if (number <= 0) {
                break;
            }
            // 每次获取到最后一个数
            numUnit = (int) (number % 10);
            if (numUnit > 0) {
                if ((numIndex == 9) && (zeroSize >= 3)) {
                    sb.insert(0, CN_UPPER_MONETRAY_UNIT[6]);
                }
                if ((numIndex == 13) && (zeroSize >= 3)) {
                    sb.insert(0, CN_UPPER_MONETRAY_UNIT[10]);
                }
                sb.insert(0, CN_UPPER_MONETRAY_UNIT[numIndex]);
                sb.insert(0, CN_UPPER_NUMBER[numUnit]);
                getZero = false;
                zeroSize = 0;
            } else {
                ++zeroSize;
                if (!(getZero)) {
                    sb.insert(0, CN_UPPER_NUMBER[numUnit]);
                }
                if (numIndex == 2) {
                    if (number > 0) {
                        sb.insert(0, CN_UPPER_MONETRAY_UNIT[numIndex]);
                    }
                } else if (((numIndex - 2) % 4 == 0) && (number % 1000 > 0)) {
                    sb.insert(0, CN_UPPER_MONETRAY_UNIT[numIndex]);
                }
                getZero = true;
            }
            // 让number每次都去掉最后一个数
            number = number / 10;
            ++numIndex;
        }
        // 如果signum == -1，则说明输入的数字为负数，就在最前面追加特殊字符：负
        if (signum == -1) {
            sb.insert(0, CN_NEGATIVE);
        }
        // 输入的数字小数点后两位为"00"的情况，则要在最后追加特殊字符：整
        if (!(scale > 0)) {
            sb.append(CN_FULL);
        }
        return sb.toString();
    }

    /**
     * @param aStr    待补充的字符串
     * @param aLength 待补充长度
     * @param a       用来补充的字符
     * @return 左补零后的数据，如 lpad("36" ,4,'0') 返回 0036
     * @description 将字符串aStr左面用字符a补足aLength长
     * @access 比较字符串的长度，如果小于指定的长度，循环在前面补充指定的字符
     * @precondition 前置条件
     * @postcondition 后置条件
     */
    public static String lpad(String aStr, int aLength, char a) {
        String rtnVal = aStr;
        int aStrLen = aStr.length();
        if (aStrLen < aLength) {
            for (int i = 0; i < aLength - aStrLen; i++) {
                rtnVal = a + rtnVal;
            }
        }
        return rtnVal;
    }

    /**
     * @param e 异常信息
     * @return 堆异信息
     * @description 获取异常堆栈信息
     * @access 根据指定的格式对异常数据进行输出
     * @precondition 无
     * @postcondition 无
     */
    public static String getStackTraceMessage(Exception e) {
        StringBuffer msg = new StringBuffer();
        msg.append(e.getClass().getName()).append(": ").append(e.getMessage())
                .append("\n\r");
        StackTraceElement[] trace = e.getStackTrace();
        for (int i = 0; i < trace.length; i++) {
            msg.append("\tat ").append(trace[i]).append("\n\r");
        }
        return msg.toString();
    }

    /**
     * @param fileName 文件名
     * @param key      键值
     * @return 键值对应的数据，异常返回为null
     * @description 读取配置文件的值
     * @access 根据key读取value
     * @precondition 文件内容存在，且按照指定格式存储数据
     * @postcondition 无
     */
    public static String getProperties(String fileName, String key) {
        Properties props = new Properties();
        // 获取文件流数据
        try (InputStream in = GgUtil.class.getClassLoader().getResourceAsStream(
                fileName)) {
            props.load(in);
            String value = props.getProperty(key);
            return value;
        } catch (Exception e) {
            // 输出异常数据
            log.error(e);
            return null;
        }
    }

    /**
     * @param s 待处理的数据
     * @return 16进制格式的大写字符串
     * @description 将字符串转十六进制大写
     * @access 依次处理每一位上的数据，然后把数据转换成大写字符，最后再将各位上的数据拼接起来
     * @precondition 格式为16进制数据格式
     * @postcondition 无
     */
    public static String strToHex(String s) {
        String str = "";
        for (int i = 0; i < s.length(); i++) {
            int ch = (int) s.charAt(i);
            String s4 = Integer.toHexString(ch).toUpperCase();
            str = str + s4;
        }
        return str;
    }

    /**
     * @param str 16进制字符串
     * @return 转换后的数据
     * @description 格式数据转换
     * @access 如果为十六进制，转换成非十六进制
     * @precondition 输入数据格式合法
     * @postcondition 无
     */
    public static String converNum(String str) {
        String regex = "E";
        Pattern pat = Pattern.compile(regex);
        Matcher mat = pat.matcher(str);
        if (mat.find()) {
            BigDecimal db = new BigDecimal(str);
            String ii = db.toPlainString();
            return ii;
        }
        return str;
    }

    /**
     * @return UUID数据
     * @description 获取UUID数据
     * @access 获取UUID数据，同时将中间的连接符去掉
     * @precondition 无
     * @postcondition 无
     */
    public static String getUUID() {
        return UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
    }

    /**
     * @param blob Blob格式的数据
     * @return 转换后的数据
     * @description 将 Blob格式的数据转换为字符串数据
     * @access 将Blob格式的数据转换为字符流，然后从字符流中读取数据数据
     * @precondition 无
     * @postcondition 无
     */
    public static String convertBlobToString(Blob blob) {
        String result = "";
        try (ByteArrayInputStream msgContent = (ByteArrayInputStream) blob
                .getBinaryStream();) {
            byte[] byte_data = new byte[msgContent.available()];
            msgContent.read(byte_data, 0, byte_data.length);
            result = new String(byte_data);
        } catch (SQLException | IOException e) {
            // 输出异常信息
            log.error(e);
        }
        return result;
    }

    /**
     * @param data 待计算的数据
     * @return MD5哈希后的数据
     * @description 对数据进行MD5哈希处理
     * @access 按照MD5算法计算哈希值
     * @precondition 无
     * @postcondition 无
     */
    public static String MD5(String data) {
        char hexDigits[] =
                {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
                        'e', 'f'};
        try {
            byte[] strTemp = data.getBytes();
            // 获取对应实例
            String method = "M#D#5";
            MessageDigest mdTemp = MessageDigest.getInstance(method.replaceAll("#", ""));
            mdTemp.update(strTemp);
            byte[] md = mdTemp.digest();
            int j = md.length;
            char str[] = new char[j * 2];
            int k = 0;
            for (int i = 0; i < j; i++) {
                byte byte0 = md[i];
                str[k++] = hexDigits[byte0 >>> 4 & 0xf];
                str[k++] = hexDigits[byte0 & 0xf];
            }
            return new String(str);
        } catch (Exception e) {
            // 输出异常信息
            log.error(e);
            return null;
        }
    }

    /**
     * @param dttm   时间数据
     * @param format 时间格式
     * @return 判断结果
     * @description 判断字符串是否为日期格式数据
     * @access 按指定的格式解析字符串数据，看是否执行成功
     * @precondition 无
     * @postcondition 无
     */
    public static boolean isDate(String dttm, String format) {
        boolean retValue = false;
        if (dttm != null) {
            SimpleDateFormat formatter = new SimpleDateFormat(format);
            formatter.setLenient(false);
            try {
                // 进行格式解析
                formatter.parse(dttm);
                retValue = true;
            } catch (ParseException e) {
            }
        }
        return retValue;
    }


    /**
     * @param pageSize 页大小
     * @param page     页号
     * @param sql      表名
     * @return sql语句
     * @throws SQLException 数据库操作异常
     * @description 获取分页sql语句
     * @access 拼接SQL语句
     * @precondition 无
     * @postcondition 无
     */
    public static String getFenYeSql(int pageSize, int page, String sql)
            throws SQLException {
        StringBuffer pageSql = new StringBuffer();
        pageSql.append("select * from (");
        pageSql.append("  select a.*, rownum xh from (").append(sql);
        pageSql.append("   ) a where rownum <= ");
        pageSql.append(page * pageSize);
        pageSql.append("  ) where xh > ");
        pageSql.append((page - 1) * pageSize);
        return pageSql.toString();
    }
    /**
     * @param s 待处理的数据1
     * @return 小数转百分数
     */
    public static String JeToBfsFormat(String s) {
        Double fromString = new Double(s);
        fromString=fromString*100;
        BigDecimal sl = strToBigDecimal(fromString.toString(), 0);
        return sl+"%";
    }
    /**
     * @param s 待处理的数据
     * @return 四舍五入后的数据
     * @description 金额四舍五入精确到小数点后两位
     * @access 指定数值格式，进行四舍五入处理
     * @precondition 金额格式进行检查
     * @postcondition 无
     */
    public static String JeFormat(String s) {
        return strToBigDecimal(s, 2).toString();
    }

    /**
     * @param map 处理之前的MAP
     * @return 处理之后的MAP
     * @description 把KEY值转换为小写
     * @access 依次遍历MAP的数据，获取每个KEY值，然后将KEY值转换为小写字符串，并设置后原MAP中对应的value值
     * @precondition 无
     * @postcondition 无
     */
    public static Map<String, String> mapToLowerMap(Map<String, String> map) {
        if (null != map && map.size() > 0) {
            Iterator it = map.entrySet().iterator();
            Map<String, String> wMap = new HashMap<String, String>();
            // 遍历MAP中的数据
            while (it.hasNext()) {
                Map.Entry entry = (Map.Entry) it.next();
                String key = (String) entry.getKey();
                String value = (String) entry.getValue();
                // 转换为小写KEY，并赋值
                wMap.put(key.toLowerCase(), value);
            }
            return wMap;
        }
        return map;
    }

    /**
     * @param length 随机数长度
     * @return 随机数对应的字符串
     * @description 生成指定长度的字符串
     * @access 定义取值区间，生成随机数，从取值区间中获取指定的数据
     * @precondition 无
     * @postcondition 无
     */
    public static String generateRandom(int length) {
        String allChar = "0123456789";
        StringBuffer sb = new StringBuffer();
        //Random random = new Random();//222
        Random random = new java.security.SecureRandom(); //222
        for (int i = 0; i < length; i++) {
            sb.append(allChar.charAt(random.nextInt(allChar.length())));
        }
        return sb.toString();
    }

    /**
     * @param str 字符串数值数据
     * @param n   进制
     * @return 大数值
     * @description 将字符串格式的数据转换为数值类型
     * @access 调用BigDecimal类方法进行处理
     * @precondition 检查字符串是否为数值格式
     * @postcondition 无
     */
    public static BigDecimal strToBigDecimal(String str, int n) {
        if ("".equals(str)) {
            str = "0";
        }
        BigDecimal bd = new BigDecimal(str);
        bd = bd.setScale(n, BigDecimal.ROUND_HALF_UP);
        return bd;
    }

    /**
     * @return 6位UUID
     * @description 生成6位随机UUID
     * @access 生成UUID，然后进行截位处理
     * @precondition 无
     * @postcondition 无
     */
    public static String generateShortUuid() {
        StringBuffer shortBuffer = new StringBuffer();
        String uuid = UUID.randomUUID().toString().replace("-", "");
        for (int i = 0; i < 8; i++) {
            String str = uuid.substring(i * 4, i * 4 + 4);
            int x = Integer.parseInt(str, 16);
            shortBuffer.append(chars[x % 0x3E]);
        }
        return shortBuffer.toString();
    }

    /**
     * @param par1 左半部分
     * @param par2 右半部分
     * @return 随机流水号
     * @description 获取随机流水号
     * @access 对入参进行MD5处理，截取指定的长度并生成流水号字符串
     * @precondition 无
     * @postcondition 无
     */
    public static String getRandomLsh(String par1, String par2) {
        String s = MD5(par1 + par2) == null ? "" : MD5(par1 + par2);
        if (s == null || "".equals(s)) {
            s = "12345678901234567890";
        }
        return s.substring(0, 20);//222
    }

    /**
     * 正则表达式：手机号
     */
    public static final String REGEX_MOBILE = "^(13[0-9]|14[5|7]|15[0-9]|17[0|1|3|5|6|7|8]|18[0-9])[0-9]{8}$";

    /**
     * 正则表达式：验证邮箱
     */
    public static final String REGEX_EMAIL = "^\\w+((-\\w+)|(\\.\\w+))*\\@[A-Za-z0-9]+((\\.|-)[A-Za-z0-9]+)*\\.[A-Za-z0-9]+$";

    /**
     * 校验手机号
     *
     * @param mobile
     * @return 校验通过返回true，否则返回false
     */
    public static boolean isMobile(String mobile) {
        return Pattern.matches(REGEX_MOBILE, mobile);
    }

    /**
     * 得到金额的大写表示字符串
     *
     * @param val double
     * @return String
     */
    public static String numToStrRMB(BigDecimal val) {
        String strHanDigi[] = new String[]{"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};
        String strSign = "";
        String strTail = "";
        long lFraction, lInteger;
        int iJiao, iFen;
        //小于0
        if (val.compareTo(BigDecimal.ZERO) == -1) {
            val = val.abs();
            strSign = "负";
        }
        if (val.compareTo(GgUtil.strToBigDecimal("99999999999999.999", 2)) == 1 || val.compareTo(GgUtil.strToBigDecimal("-99999999999999.999", 2)) == -1) {
            return "数值位数过大!";
        }
        // 四舍五入到分
        long lTemp = Math.round(val.doubleValue() * 100);
        lInteger = lTemp / 100;
        lFraction = lTemp % 100;
        iJiao = (int) lFraction / 10;
        iFen = (int) lFraction % 10;
        if (iJiao == 0 && iFen == 0) {
            strTail = "整";
        } else {
            strTail = strHanDigi[iJiao];
            if (iJiao != 0) {
                strTail += "角";


            }
            // 零元后不写零几分
            if (lInteger == 0 && iJiao == 0) {
                strTail = "";
            }
            if (iFen != 0) {
                strTail += strHanDigi[iFen] + "分";
            }
        }
        return strSign + positiveIntegerToHanStr(StringUtil.toString(lInteger)) + "圆" + strTail;
    }

    /**
     * 得到金额的大写表示字符串,处理整数部分
     *
     * @param strNumber String
     * @return String
     */
    private static String positiveIntegerToHanStr(String strNumber) { // 输入字符串必须正整数，只允许前导空格(必须右对齐)，不宜有前导零

        String strHanDigi[] = new String[]{"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};

        String strHanDivi[] = new String[]{"", "拾", "佰", "仟", "万", "拾", "佰", "仟", "亿", "拾", "佰", "仟", "万", "拾", "佰", "仟", "亿", "拾", "佰", "仟", "万", "拾", "佰", "仟"};

        String strRMB = "";
        boolean bLastzero = false;
        // 亿、万进位前有数值标记
        boolean bHasvalue = false;
        int iLength, iNum;
        iLength = strNumber.length();
        if (iLength > 15) {
            return "数值过大!";
        }
        for (int i = iLength - 1; i >= 0; i--) {
            if (strNumber.charAt(iLength - i - 1) == ' ') {
                continue;
            }
            iNum = strNumber.charAt(iLength - i - 1) - '0';
            if (iNum < 0 || iNum > 9) {
                return "输入含非数字字符!";
            }
            if (iNum != 0) {
                if (bLastzero) {
                    strRMB += strHanDigi[0]; // 若干零后若跟非零值，只显示一个零
                }

                strRMB += strHanDigi[iNum];

                strRMB += strHanDivi[i]; // 非零值后加进位，个位为空
                bHasvalue = true; // 置万进位前有值标记
            } else {
                // 亿万之间必须有非零值方显示万
                if ((i % 8) == 0 || ((i % 8) == 4 && bHasvalue)) {
                    strRMB += strHanDivi[i]; // “亿”或“万”
                }
            }
            if (i % 8 == 0) {
                bHasvalue = false; // 万进位前有值标记逢亿复位
            }
            bLastzero = (iNum == 0) && (i % 4 != 0);
        }
        if (strRMB.length() == 0) {
            return strHanDigi[0]; // 输入空字符或"0"，返回"零"
        }
        return strRMB;
    }

    /**
     * 校验邮箱
     *
     * @param email
     * @return 校验通过返回true，否则返回false
     */
    public static boolean isEmail(String email) {
        return Pattern.matches(REGEX_EMAIL, email);
    }

}
